package com.atguigu.tingshu.order.adapter.impl;

import java.util.List;

import com.atguigu.tingshu.account.client.UserAccountFeignClient;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.rabbit.constant.MqConst;
import com.atguigu.tingshu.common.rabbit.service.RabbitService;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.model.order.LocalMsg;
import com.atguigu.tingshu.order.adapter.PayWay;
import com.atguigu.tingshu.order.mapper.LocalMsgMapper;
import com.atguigu.tingshu.order.service.OrderInfoService;
import com.atguigu.tingshu.vo.account.AccountLockResultVo;
import com.atguigu.tingshu.vo.account.AccountLockVo;
import com.atguigu.tingshu.vo.order.OrderInfoVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

/**
 * @author codingyz
 * @Description
 * @date 2025/3/28 18:57
 * @Version 1.0
 */
@Service
public class RechargePayWayImpl implements PayWay {

    @Autowired
    private OrderInfoService orderInfoService;

    @Autowired
    private UserAccountFeignClient userAccountFeignClient;

    @Autowired
    private RabbitService rabbitService;

    @Autowired
    private LocalMsgMapper localMsgMapper;

    @Autowired
    private StringRedisTemplate redisTemplate;


    String unlockMsgCacheKey = "";
    String minusMsgCacheKey = "";

    @Scheduled(fixedDelay = 1000 * 60 * 60) // 每1小时检查一次
    public void scanLocalStatusAndRetryMsg() {
        // 查询本地消息表
        List<LocalMsg> localMsgList = localMsgMapper.selectList(new LambdaQueryWrapper<LocalMsg>().eq(LocalMsg::getStatus, 0));
        for (LocalMsg localMsg : localMsgList) {
            // 维护一个本地消息表 用于判断要不要消息重发 状态为0则需要重发 为1则不需要
            String unlockKey = redisTemplate.opsForValue().get(unlockMsgCacheKey);
            String minusKey = redisTemplate.opsForValue().get(minusMsgCacheKey);
            if (StringUtils.hasText(unlockKey)) {
                rabbitService.sendMessage(MqConst.EXCHANGE_ACCOUNT, MqConst.ROUTING_ACCOUNT_UNLOCK, localMsg.getMsgContent());
            }
            if (StringUtils.hasText(minusKey)) {
                rabbitService.sendMessage(MqConst.EXCHANGE_ACCOUNT, MqConst.ROUTING_ACCOUNT_MINUS, localMsg.getMsgContent());
            }
        }
    }

    @Override
    public boolean isSupport(String payWay) {
        return "1103".equals(payWay);
    }

    /**
     * todo 分布式事物的问题:
     * 1.rpc :利用异常机制反向修改(Seata)
     * 2.mq : 利用本地消息表+定时任务
     *
     * @param orderInfoVo
     * @param userId
     * @param orderNo
     */
    @Override
    public void dealPayWay(OrderInfoVo orderInfoVo, Long userId, String orderNo) {
        // 零钱支付逻辑

        // 1.零钱是否充足

        // 2.充足-->保存相关订单信息
        // 2.1 远程调用account微服务 查询用户余额
        AccountLockVo userAccountVo = prepareUserAccountVo(orderNo, userId, orderInfoVo);
        Result<AccountLockResultVo> accountLockResultVoResult = userAccountFeignClient.checkAndLockAmount(userAccountVo); // 查询余额是否足够并锁定余额
        if (accountLockResultVoResult.getCode() != 200) {
            throw new GuiguException(ResultCodeEnum.ACCOUNT_LOCK_ERROR);
        }
        try {
            initLocalMsg(orderNo);
            orderInfoService.saveOrderInfo(userId, orderInfoVo, orderNo); // 如果此处出异常 锁定余额已经成功 则需要回滚account库 涉及到分布式事物
            // 3.消费-->真正的扣减余额 使用mq异步和openfeign都可以 此处使用mq异步(学习手动处理分布式事物)
            rabbitService.sendMessage(MqConst.EXCHANGE_ACCOUNT, MqConst.ROUTING_ACCOUNT_MINUS, orderNo);

            // 4.处理零钱支付成功之后
            orderInfoService.paySuccess(orderInfoVo, userId, orderNo);
        } catch (GuiguException e) {
            // 接到orderInfoService抛出的异常 利用异常机制反向操作account库
            try {
                rabbitService.sendMessage(MqConst.EXCHANGE_ACCOUNT, MqConst.ROUTING_ACCOUNT_UNLOCK, orderNo); // 账户解锁 需要保证100%成功 采用定时任务
                unlockMsgCacheKey = "unlock:msg:fail:flag:" + orderNo;
                redisTemplate.opsForValue().set(unlockMsgCacheKey, "1");
            } catch (Exception ex) {
                rabbitService.sendMessage(MqConst.EXCHANGE_ACCOUNT, MqConst.ROUTING_ACCOUNT_UNLOCK, orderNo);
                unlockMsgCacheKey = "unlock:msg:fail:flag:" + orderNo;
                redisTemplate.opsForValue().set(unlockMsgCacheKey, "1");
            }
        } catch (Exception e) {
            // mq异步发消息 出现异常(消费) 需要保证100%成功 采用定时任务

            rabbitService.sendMessage(MqConst.EXCHANGE_ACCOUNT, MqConst.ROUTING_ACCOUNT_MINUS, orderNo);
            minusMsgCacheKey = "minus:msg:fail:flag:" + orderNo;
            redisTemplate.opsForValue().set(minusMsgCacheKey, "1");
        }


    }

    private void initLocalMsg(String orderNo) {
        LocalMsg localMsg = new LocalMsg();
        localMsg.setMsgContent(orderNo);
        localMsg.setStatus(0);
        localMsgMapper.insert(localMsg);
    }

    /**
     * 封装用户账户信息
     *
     * @return
     */
    private AccountLockVo prepareUserAccountVo(String orderNo, Long userId, OrderInfoVo orderInfoVo) {
        AccountLockVo userAccountVo = new AccountLockVo();
        userAccountVo.setOrderNo(orderNo);
        userAccountVo.setUserId(userId);
        userAccountVo.setAmount(orderInfoVo.getOrderAmount());
        userAccountVo.setContent(orderInfoVo.getOrderDetailVoList().get(0).getItemName());

        return userAccountVo;
    }
}
